Function.RegisterNamespace("xUnit.js.Console.Output.OutputStrategy");

xUnit.js.Console.Output.OutputStrategy.Text=function(){
    // Private Members
    var _errors=[];

    // IOutputStrategy Members
    this.OutputLevel="";

    this.Prologue=function(){
        System.Environment.Write("xUnit.js Console Runner");
    };

    this.Epilogue=function(){
        System.Environment.Write('\nDone.\n\n');
    };
    
    this.BeginFileLoad=function(){
        var startTime=new Date();
        System.Environment.Write("\nLoading Scripts:\n");
    };

    this.FileLoadSuccess=function(file,duration){
        listFile(file,System.Environment.GetWorkingDirectory());
    };
    
    this.CompleteFileLoad=function(files,duration){
        if(files.length)System.Environment.Write('\n\n');
        System.Environment.Write(String.Format("Finished Loading Scripts in {0}.\n",formatDuration(duration)));
    };

    this.BeginRun=function(){
        System.Environment.Write("\nRunning tests:\n\n");    
    };
    
    this.CompleteRun=function(successes,failures,errors,warnings,skipped,duration){
        System.Environment.Write("\n");
        if(failures.length){
            System.Environment.Write("\nFailed Tests:\n");
            Array.ForEach(failures,reportFailure);            
        }
        if(errors.length){
            System.Environment.Write("\nErrored Tests:\n");
            Array.ForEach(errors,reportFailure);            
        }
        if(warnings.length){
            System.Environment.Write("\nWarnings:\n");
            Array.ForEach(warnings,reportFailure);
        }
        if(_errors.length){
            System.Environment.Write("\nScript Errors:\n");
            Array.ForEach(_errors,reportError);            
        }
        if(skipped.length){
            System.Environment.Write("\nSkipped Tests:\n");
            Array.ForEach(skipped,reportSkipped);            
        }
        var count=successes.length+failures.length+errors.length+warnings.length+skipped.length;
        var issues=failures.length+errors.length+warnings.length+_errors.length;
        if(issues==0)System.Environment.Write("\n");
        System.Environment.Write(String.Format("Total tests: {0}, Errors: {1}, Failures: {2}, Warnings: {3}, Skipped: {4}, Time: {5}\n\n",count,errors.length+_errors.length,failures.length,warnings.length,skipped.length,formatDuration(duration)));
    };
    
    this.BeginComponent=function(component){};

    this.CompleteComponent=function(component,duration){
        if(!Object.IsType(xUnit.js.Model.Fact,component))return;
        var result='';
        switch(component.State.Result){
            case xUnit.js.Model.Result.Error:
                result='E';
                break;
            case xUnit.js.Model.Result.Failure:
                result='F';
                break;
            case xUnit.js.Model.Result.Skipped:
                result='S';
                break;
            case xUnit.js.Model.Result.Success:
                result='.';
                break;
            case xUnit.js.Model.Result.Warning:
                result='W';
                break;
            default:
                result='?';
                break;
        }
        System.Environment.Write(result);
    };

    this.Enumerate=function(component){
        System.Environment.Write(getPath(component));
        System.Environment.Write('\r\n');
    };

    this.Error=function(error){
        _errors.push(error);
    }
    
    // IStrategySpecification Members
    this.IsSatisfiedBy=function(candidate){
        return String.Equals(xUnit.js.Console.Output.OutputFormatter.OutputTypes.text,candidate);
    };
    
    // Private Methods    
    function formatDuration(duration){
        if(!Object.IsType(Number,duration))return "an unknown amount of time";
        duration=duration/1000;
        var components={
            hours:{
                format:"{0} hour{1}",
                value:Math.floor(duration/60/60)
            },
            minutes:{
                format:"{0} minute{1}",
                value:Math.floor(duration/60)%60
            },
            seconds:{
                format:"{0} second{1}",
                value:duration%60
            }
        };
        var result=[];
        for(var x in components){
            var value=components[x].value;
            if(value||x=="seconds"&&!result.length)result.push(String.Format(components[x].format,value,value!=1?'s':''));
        }
        return result.join(", ");
    }

    function formatError(error){
        return error&&error.toString(isVerbose())||"[No Message]";
    }

    function getPath(fact){
        return fact.GetPath().split('.').slice(1).join('.');
    }
    
    function isVerbose(){
        return this.OutputLevel==xUnit.js.Console.Output.OutputFormatter.OutputLevels.verbose;
    }
    isVerbose=Function.GetDelegate(isVerbose,this);

    function listFile(file,workingDirectory){
        var fileName=file;
        if(String.StartsWith(fileName,workingDirectory)){
            fileName=fileName.substr(workingDirectory.length+1);
        }
        System.Environment.Write('\n\t',fileName);
    }

    function reportError(error,context){
        System.Environment.WriteError(String.Format("\t{0}) {1}\n\n",context.Index+1,formatError(error)));    
    }

    function reportFailure(run,context){
        var msg=[
            String.Format("{0}{1} -- {2}",getPath(run.Component),run.Component.Data?String.Format(":[{0}]",run.State.Index):'',run.State.Message&&run.State.Message.name||"Exception"),
            isVerbose()?String.Format(" ({0}): ",run.Component.File):': ',
            formatError(run.State.Message)
        ].join('');
        var err=String.Format("\t{0}) {1}\n\n",context.Index+1,msg);
        System.Environment.WriteError(err);
    }

    function reportSkipped(run,context){
        var msg=String.Format("{0} -- {1}",getPath(run.Component),run.State.Message||"[no message]");
        System.Environment.Write(String.Format("\t{0}) {1}\n\n",context.Index+1,msg));
    }    

};

xUnit.js.Console.Output.OutputStrategy.Text.Implement(xUnit.js.Console.Output.IOutputStrategy,'xUnit.js.Console.Output.OutputStrategy.Text');
xUnit.js.Console.Output.OutputStrategy.Text.Implement(System.Script.Strategy.IStrategySpecification,'xUnit.js.Console.Output.OutputStrategy.Text');